const Queue = require('bull');
const net = require('net');
const fs = require('fs');
const crypto = require('crypto');
const axios = require('axios');

const server = new net.Server();

let processes = new Queue("processes", {});

let journal = [];
let processes_friendly = [];
let memory_max = 8000000;
let memory_used = 0;
let config = JSON.parse(fs.readFileSync("./config.json", 'utf8'));

processes.on("active", (job) => {
    console.log(`[${job.id}] Job with the ID ${job.id} running.`);
    processes_friendly[processes_friendly.indexOf(`${job.data.identify}::${job.data.owner}`)] = `${job.data.identify}::${job.data.owner} (WORKING)`;
})

processes.on("completed", (job) => {
    console.log(`[${job.id}] Job with the ID ${job.id} completed.`);
    processes_friendly.splice(processes_friendly.indexOf(`${job.data.identify}::${job.data.owner} (WORKING)`));
    memory_used += -7092;
})

processes.on("failed", (job) => {
    processes_friendly[processes_friendly.indexOf(`${job.data.identify}::${job.data.owner}`)] = `${job.data.identify}::${job.data.owner} (FAILED)`;
    console.log(job.failedReason);
    job.remove();
});

processes.process(async(job, done) => {
    let memory = Array(7092).fill('0');
    let last_used_unit = 0;

    memory_used += 7092;

    for (let x = 0; x < job.data.commands.length; x++){
        const __data = job.data.commands[x].split(' ');
        if (__data[0] == "__set_fv"){memory[last_used_unit] = __data[1]}
        if (__data[0] == "__del_fv"){memory[last_used_unit] = __data[1]}
        if (__data[0] == "__mov_fv"){memory[last_used_unit] = memory[__data[2]]}
        if (__data[0] == "__clr_fv"){memory[last_used_unit] = 0}
        if (__data[0] == "__set_ps"){if (__data[1] > memory.length){} else {last_used_unit = __data[1]}}
        if (__data[0] == "__add_fv"){memory[last_used_unit] = memory[__data[1]] + memory[last_used_unit];}
        if (__data[0] == "__tst_fv"){
            if (__data[1] > job.data.commands.length || __data[1] < -1){} else {
                if (memory[last_used_unit] == memory[__data[1]]){
                    x++;
                } else {
                    x += 2;
                }
            }
        }
        if (__data[0] == "__jmp_mt"){if (Number(__data[1]) > -1 && Number(__data[1]) < job.data.commands.length){x = __data[1];} else {
            done();
        }}
    };

    fs.writeFileSync(`harddrive/home/${job.data.owner}/${job.data.identify}.log`, memory.toString());
    done();
});

server.on("connection", (server) => {
    let username = "";

    server.on("error", (err) => {
        console.log([err.message, err.name]);
    });
    server.on("data", (data) => {
        try {
            config = JSON.parse(fs.readFileSync("./config.json", 'utf8'));
            const __data = data.toString('utf8').trim().split(" ");
            let cache = "";
    
            console.log(__data[0]);
    
            if (username != "")
            {
                journal += `[LOG] ${__data.toString()}\n`;
                if (__data[0] == "journalctl" && config['users'][username]['groups'].includes('net') && config['users'][username]['groups'].includes('root'))
                {
                    journal.forEach(log => {
                        server.write(`${log}\r\n`, 'utf8');
                    });
                }
                if (__data[0] == "free")
                {
                    server.write(`${memory_used} KB / ${memory_max} KB`, 'utf8');
                }
                if (__data[0] == "echo")
                {
                    for (let x = 1; x < __data.length; x++)
                    {
                        cache += __data[x] + " ";
                    }
                    server.write(cache + "\r\n", 'utf8')
                }
        
                if (__data[0] == "download" && config['users'][username]['groups'].includes('net') && config['users'][username]['groups'].includes('filesystem'))
                {
                    let url_parsed = __data[1].split('/');
                    let filename = url_parsed[url_parsed.length - 1];
    
                    server.write("Downloading... (Just wait!)\r\n");
    
                    axios({
                        method: "get",
                        url: __data[1],
                        responseType: "stream"
                    }).then( (response) => {
                        response.data.pipe(fs.createWriteStream(`./harddrive/home/${username}/${filename}`));
                    });
                    server.write("Downloaded!\r\n", 'utf8');
                }
        
                if (__data[0] == "email" && config['users'][username]['groups'].includes('email'))
                {
                    if (__data[1] == "read")
                    {
                        server.write(JSON.stringify(config['users'][username]['emails']) + "\r\n", 'utf8');
                    }
    
                    if (__data[1] == "del")
                    {
                        if (config['users'][username]['emails'][__data[2]] != undefined){
                            server.write("Deleting...\r\n", 'utf8');
                            delete config['users'][username]['emails'][__data[2]];
                            server.write("Done!\r\n", 'utf8');
                        } else {
                            server.write("An email is not exists in this mailbox.\r\n");
                        }
                    }
    
                    if (__data[1] == "compose")
                    {
                        if (__data[2] == undefined)
                        if (__data[3] == undefined)
                        if (__data[4] == undefined)
                        if (config['users'][__data[2]] == undefined){return server.write("The user is not exists inside a database.\r\n", 'utf8');}
    
                        config['users'][__data[2]]['emails'][__data[3]] = {};
                        config['users'][__data[2]]['emails'][__data[3]] = {
                            "title": __data[3],
                            "from": username,
                            "content": __data[4]
                        };
    
                        server.write("Email sent.\r\n", 'utf8');
                    }
                }
                if (__data[0] == "pes"){
                    
                    if (memory_used == memory_max || memory_used > memory_max){
                        return server.write("Too many processes running right now. OOM (-OUT_OF_MEMORY)", 'utf8');
                    }
    
                    if (!fs.existsSync(`./harddrive/home/${username}/${__data[1]}`)){return server.write("requested file is not exists or is this a toaster.\r\n", 'utf8')}
    
                    const script = fs.readFileSync(`./harddrive/home/${username}/${__data[1]}`, 'utf8');
                    const commands = script.split('\n');
    
                    let __randomUUID = crypto.randomBytes(16).toString('base64url');
                    const process = processes.add({commands: commands, identify: __randomUUID, owner: username});
    
                    processes_friendly.push(`${__randomUUID}::${username}`);
    
                    server.write(`ID : ${__randomUUID}\r\n`, 'utf8')
                }
    
                if (__data[0] == "processes")
                {
                    server.write("[PROCESS] [OWNER] [STATUS]\r\n", 'utf8');
                    for (let x = 0; x < processes_friendly.length; x++){
                        const __data__ = processes_friendly[x].split('::');
                        const process_owner = __data__[1];
                        const process_target = __data__[0];
    
                        server.write(`${process_target} ${process_owner}\r\n`, 'utf8');
                    };
                }
    
                if (__data[0] == "bye")
                {
                    server.write("Goodbye!\r\n");
                    server.destroy();
                }
    
                if (__data[0] == "ls" && config['users'][username]['groups'].includes('filesystem'))
                {
                    const __files = fs.readdirSync(`./harddrive/home/${username}/`, 'utf8');
    
                    __files.forEach(file => {
                        server.write(`${file}\r\n`);
                    });
                }
    
                if (__data[0] == "del" && config['users'][username]['groups'].includes('filesystem'))
                {
                    __data[1] = __data[1].replace("../", '');
                    if (fs.existsSync(`./harddrive/home/${username}/${__data[1]}`))
                    {
                        fs.rmSync(`./harddrive/home/${username}/${__data[1]}`);
                        server.write(`The file has been successfully removed.\r\n`, 'utf8');
                    } else {
                        server.write(`The file is not exists.\r\n`, 'utf8');
                    }
                }
    
                if (__data[0] == "touch" && config['users'][username]['groups'].includes('filesystem'))
                {
                    __data[1] = __data[1].replace("../", '');
                    if (fs.existsSync(`./harddrive/home/${username}/${__data[1]}`))
                    {
                        server.write("The file is already exists.\r\n", 'utf8');
                    } else {
                        fs.writeFileSync(`./harddrive/home/${username}/${__data[1]}`, '');
                        server.write("The file was successfully created.\r\n", 'utf8');
                    }
                }
    
                if (__data[0] == "cat" && config['users'][username]['groups'].includes('filesystem'))
                {
                    __data[1] = __data[1].replace("../", '');
    
                    if (fs.existsSync(`./harddrive/home/${username}/${__data[1]}`))
                    {
                        const data = fs.readFileSync(`./harddrive/home/${username}/${__data[1]}`, 'utf8');
                        server.write(`${data}\r\n`, 'utf8');
                    }
                }
    
                if (__data[0] == "gpuinfo" && config['users'][username]['groups'].includes('gpu'))
                {
                    config['deviceInfo']['gpus'].forEach(gpu => {
                        server.write(`${gpu}\r\n`, 'utf8');
                    });
                }
    
                if (__data[0] == "adduser" && config['users'][username]['groups'].includes('root'))
                {
                    try {
                        if (config['users'][__data[1]]){server.write(`The User is already exists!`);} else {
                            config['users'][__data[1]] = {};
                            config['users'][__data[1]]['emails'] = {};
                            config['users'][__data[1]]['password'] = __data[2];
                            config['users'][__data[1]]['groups'] = ["user"];
                            server.write(`OK\r\n`, 'utf8');
                        }
                    } catch (error) {
                        server.write(`Error!\r\n`, 'utf8');
                    }
                }

                if (__data[0] == "deluser" && config['users'][username]['groups'].includes('root'))
                {
                    try {
                        if (config['users'][__data[1]]){delete config['users'][__data[1]]; server.write(`OK\r\n`, 'utf8');} else {server.write(`The user is not exists.\r\n`, 'utf8');}
                    } catch (error) {
                        server.write(`Error!\r\n`, 'utf8');
                    }
                }

                if (__data[0] == "groupmodify" && config['users'][username]['groups'].includes('root'))
                {
                    try {
                        if (config[users][__data[1]]['groups'].includes(__data[1])){delete config[users][__data[1]]['groups'][__data[1]]; server.write(`Group removed.`, 'utf8')} else {
                            config[users][__data[1]]['groups'].push(__data[2]);
                            server.write(`Group added.\r\n`, 'utf8');
                        }
                    } catch (error) {
                        server.write(`Error!\r\n`, 'utf8');
                    }
                }
    
                if (__data[0] == "help")
                {
                    if (!__data[1])
                    {
                        const commands = ["echo", "download", "email", "pes", "processes", "bye", "ls", "del", "touch", "cat", "adduser", "deluser", "groupmodify", "free"];

                        commands.forEach(command => {
                            server.write(command, 'utf8');
                        });
                    }
                    if (__data[1] == "echo"){server.write(`echo - Are you seriously don't know about "echo"?! Well. It's echo.\r\n`);}
                    if (__data[1] == "download"){server.write(`Arguments: %url% (Sample : https://google.com/index.html)\nDescription: download - Downloading files in the internet. Aren't you using this OS only because you downloaded it?\r\n`);}
                    if (__data[1] == "email"){server.write(`Arguments: read, compose, del\nDescription: Like a real mail, gives you a way to receive, send and delete mails inside your mailbox.\r\n`);}
                    if (__data[1] == "pes"){server.write(`Arguments: %filename%\nDescription: Scripts, math scripts and much more. Compatible with GPUs!\r\n`);}
                    if (__data[1] == "processes"){server.write(`processes - List of active processes of another users and your own.\r\n`);}
                    if (__data[1] == "bye"){server.write(`bye - https://tinyurl.com/2av49kcf`);}
                    if (__data[1] == "ls"){server.write(`Displays all files in the directory. Yeah, you can't create folders. Why we're need a folders?\r\n`)}
                    if (__data[1] == "del"){server.write(`Arguments: %filename%\nDescription: Deletes a file.\r\n`);}
                    if (__data[1] == "touch"){server.write(`Arguments: %filename%\nDescription: Creates a new file.\r\n`);}
                    if (__data[1] == "cat"){server.write(`Arguments: %filename%\nDescription: Just a cat that reads your mails, documents and does it for you.\r\n`);}
                    if (__data[1] == "adduser"){server.write(`Arguments: %username%\nDescription: Add a user.\r\n`);}
                    if (__data[1] == "deluser"){server.write(`Arguments: %username%\nDescription: Remove a user.\r\n`);}
                    if (__data[1] == "groupmodify"){server.write(`Arguments: %username% %group%\nDescription: Removes and add new groups.\r\n`);}
                    if (__data[1] == "free"){server.write(`Free and used memory.`);}
                }
            } else {
                if (__data[0] != "login"){server.write("You need to login first. Use command 'login %username% %password%'!\r\n", 'utf8');} else {
                    try {
                        if (config['users'][__data[1]] == undefined){server.write("The user is not exists inside a database.\r\n"); server.destroy();}
                        if (config['users'][__data[1]]['password'] == undefined){server.write("Hey! You forgot to type a password.\r\n"); server.destroy();}
                        if (config['users'][__data[1]]['password'] != __data[2]){server.write("The password is different than the user's actual password.\r\n"); server.destroy();}    
                        if (!config['users'][__data[1]]['groups'].includes('login-allowed')){server.write("The login has been rejected due to the your account block. You need to contact with this server owner.", 'utf8'); server.destroy();}
    
                        username = __data[1];
                        server.write(`Hello, ${__data[1]}!\r\n`, 'utf8');
                    } catch (error) {
                        
                    }
                }
            }
        
            fs.writeFileSync("config.json", JSON.stringify(config, null, 2));
            cache = "";
    
            server.write(`${username}#`, 'utf8');
        } catch (error) {
            console.log(error);
        }
    });
});

server.listen(3200, '0.0.0.0');